/*
 * @(#)GOHandle.java     14/08/02
 * 
 * Author             Rick Wilson
 * Copyright (c) 2014 Rick Wilson
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of 'Taareekah' nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */



package gameobject;

//~--- non-JDK imports --------------------------------------------------------

import core.Main;

import gameobject.GOManager.GameObject;

//~--- JDK imports ------------------------------------------------------------

import java.io.Serializable;

import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A reference to a {@linkplain gameobject.GOManager.GameObject GameObject}.
 * @author rick
 */
public class GOHandle implements Cloneable, Serializable {

    /** Field description */
    private UUID	go_id;

    /**
     * Empty constructor.
     */
    public GOHandle() {
	go_id	= null;
    }

    /**
     * Sets id to the id of handle's owner.
     * @param handle A component.
     */
    public GOHandle(Component handle) {
	if ((handle == null) || (handle.getOwner() == null)) {
	    go_id	= null;
	} else {
	    go_id	= handle.getOwner().getID();
	}
    }

    /**
     * Sets id to handle's id or null if handle is invalid.
     * @param handle A GameObject.
     */
    public GOHandle(GameObject handle) {
	if (handle == null) {
	    go_id	= null;
	} else {
	    go_id	= handle.getUUID();
	}
    }

    /**
     * Copy constructor. Doesnt check validity.
     * @param handle The handle to copy.
     */
    public GOHandle(GOHandle handle) {
	if (handle == null) {
	    go_id	= null;
	} else {
	    go_id	= handle.getID();
	}
    }

    /**
     * Point this handle to GO 'id'. Doesnt check validity.
     * @param id
     */
    public GOHandle(UUID id) {
	go_id	= id;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public GOHandle clone() {
	return clone();
    }

    /**
     * Returns the id of the tracked GO.
     * @return The id.
     */
    public UUID getID() {
	return go_id;
    }

    /**
     * Nothing so far.
     * @return false
     */
    public boolean isValid() {
	return false;
    }

    /**
     * Resets the tracked id.
     * @param id The new id.
     */
    public void set(UUID id) {
	go_id	= id;
    }

    /**
     * Resets the tracked id.
     * @param id The new id.
     */
    public void set(GOHandle id) {
	if (id == null) {
	    go_id	= null;
	} else {
	    go_id	= id.getID();
	}
    }

    /**
     * Resets the tracked id.
     * @param id The new id.
     */
    public void set(GameObject id) {
	if (id == null) {
	    go_id	= null;
	} else {
	    go_id	= id.getUUID();
	}
    }

    /**
     * Checks if tracked GO has a component of type 'name'.
     * @param name Name of component.
     * @return True if it does false otherwise.
     * @throws Exception If the tracked object is invalid.
     */
    public boolean hasComponent(String name) throws Exception {
	return getObject().hasComponent(name);
    }

    /**
     * Get the component of type 'name' from tracked GO.
     * @param name Component name to retrieve.
     * @return The component.
     * @throws Exception If GO is invalid or it doesn't have component.
     */
    public Component getComponent(String name) throws Exception {
	try {
	    return getObject().getComponent(name);
	} catch (Exception e) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, "Couldnt get component {0}", name);

	    throw e;
	}
    }

    /**
     * Returns the actual object identified by the id we are tracking.
     * @return The GameObject tracked.
     * @throws Exception If the id is invalid.
     */
    public GameObject getObject() throws Exception {
	try {
	    return Main.getWorld().getGOManager().getObjectFromUUID(go_id);
	} catch (Exception e) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, "Couldnt get object {0}", e);

	    throw e;
	}
    }

    /**
     * Method description
     *
     *
     * @param name
     *
     * @return
     */
    private String[] parseComponentNameAndSubName(String name) {
	String[]	split	= name.split("[.]", 2);

	if (split.length != 2) {
	    return new String[] { "", "" };
	}

	return split;
    }

    /*
     * The name string should be in the form "componentname.attributename"
     */

    /**
     * Checks if attribute exists on a component. The name string should be in the form "componentname.attributename"
     * @param name The name string should be in the form "componentname.attributename"
     * @return True if it exists, false otherwise
     */
    public boolean hasAttribute(String name) {
	String[]	component_name_subname	= parseComponentNameAndSubName(name);

	try {
	    if (getComponent(component_name_subname[0]).hasAttribute(component_name_subname[1])) {
		return true;
	    }
	} catch (Exception ex) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, null, ex);

	    return false;
	}

	return false;
    }

    /**
     * Checks if method exists on a component. The name string should be in the form "componentname.methodname"
     * @param name The name of the method
     * @return True if it exists, false otherwise.
     */
    public boolean hasMethod(String name) {
	String[]	component_name_subname	= parseComponentNameAndSubName(name);

	try {
	    if (getComponent(component_name_subname[0]).hasMethod(component_name_subname[1])) {
		return true;
	    }
	} catch (Exception ex) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, null, ex);

	    return false;
	}

	return false;
    }

    /**
     * Calls a method on a component with argument args. The name string should be in the form "componentname.methodname"
     * @param name The component and method name.
     * @param args An argument to pass.
     * @return Whatever the method returns.
     * @throws Exception If GO is invalid, component or method doesnt exist.
     */
    public Object callMethod(String name, Object args) throws Exception {
	try {
	    String[]	component_name_subname	= parseComponentNameAndSubName(name);

	    return getComponent(component_name_subname[0]).getMethod(component_name_subname[1]).call(args);
	} catch (Exception e) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, "Couldnt call method {0}", name);

	    throw e;
	}
    }

    /**
     * Calls a method on a component with arguments args. The name string should be in the form "componentname.methodname"
     * @param name The component and method name.
     * @param args  arguments to pass.
     * @return Whatever the method returns.
     * @throws Exception If GO is invalid, component or method doesnt exist.
     */
    public Object callMethod(String name, Object... args) throws Exception {
	try {
	    String[]	component_name_subname	= parseComponentNameAndSubName(name);

	    return getComponent(component_name_subname[0]).getMethod(component_name_subname[1]).call(args);
	} catch (Exception e) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, "Couldnt call method {0}", name);

	    throw e;
	}
    }

    /**
     * Calls a method with no parameters. The name string should be in the form "componentname.methodname"
     * @param name The component and method name.
     * @return Whatever the method returns.
     * @throws Exception If GO is invalid, component or method doesnt exist.
     */
    public Object callMethod(String name) throws Exception {
	try {
	    String[]	component_name_subname	= parseComponentNameAndSubName(name);

	    return getComponent(component_name_subname[0]).getMethod(component_name_subname[1]).call();
	} catch (Exception e) {
	    Logger.getLogger(GOHandle.class.getName()).log(Level.SEVERE, "Couldnt call method {0}", name);

	    throw e;
	}
    }

    /**
     * Gets the value of attribute. The name string should be in the form "componentname.methodname"
     * @param name Name of the component and attribute.
     * @return The value of the attribute.
     * @throws Exception If component or attribute doesnt exist.
     */
    public Object getAttribute(String name) throws Exception {
	String[]	component_name_subname	= parseComponentNameAndSubName(name);

	return getComponent(component_name_subname[0]).getAttribute(component_name_subname[1]);
    }

    /**
     * Gets the value of attribute. The name string should be in the form "componentname.methodname"
     * @param name Component and attribute name.
     * @param default_value A default value to return on failure.
     * @return The value of the attribute or default_value on failure
     */
    public Object getAttribute(String name, Object default_value) {
	String[]	component_name_subname	= parseComponentNameAndSubName(name);

	try {
	    return getComponent(component_name_subname[0]).getAttribute(component_name_subname[1]);
	} catch (Exception e) {
	    return default_value;
	}
    }

    /**
     * Sets the value of an attribute. The name string should be in the form "componentname.methodname"
     * @param name Component and attribute name.
     * @param obj The value.
     * @throws Exception If GO is invalid or if attribute doesnt exist.
     */
    public void setAttribute(String name, Object obj) throws Exception {
	if (!hasAttribute(name)) {
	    throw new Exception("Invalid attribute");
	}

	String[]	component_name_subname	= parseComponentNameAndSubName(name);

	getComponent(component_name_subname[0]).setAttribute(component_name_subname[1], obj);
    }

    /**
     * Calls deregister on tracked GO.
     */
    public void deregister() {
	try {
	    getObject().deregister();
	} catch (Exception e) {}
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
